﻿#include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/asio/steady_timer.hpp>

template<typename T>
void deleter(T* ptr) {
	std::cout << "智能指针回收" << std::endl;
	delete ptr;
	ptr = nullptr;
}
using namespace std;
using namespace boost;
using namespace boost::asio;
typedef ip::tcp::acceptor acceptor_type;
typedef ip::tcp::endpoint endpoint_type;
typedef ip::tcp::socket socket_type;
typedef std::shared_ptr<socket_type> socket_ptr;
typedef vector<char> buffer_type;
const string suffix("end");//数据包后缀，完整数据结束标志，因为tcp数据包是粘连的。
const string delims(" ,\t;");//数字之间的分隔符，可以空格、逗号、tab或者分号
const string ipaddress("0.0.0.0");//服务侦听ip
const int port(8001);
const std::chrono::seconds deadline(3);//超时设定，3秒没数据交互，服务器主动踢出客户端

class Peer {//客户端建立连接，对应一个Peer类实例
	typedef Peer this_type;
private:
	socket_ptr socket;
	buffer_type m_buf;//数据接收缓冲区
	string recvData;//接收到的数据，客户端发来的
	string respData;//返回的数据，反馈回客户端的
	string peerName;//本节点的名字
	std::chrono::system_clock::time_point lasttime;//最近一次活动时间，实现客户端静默超时，服务端主动关闭连接
	string state;//状态标记，“close”表示本节点已经关闭
	string respondStr;
public:
	Peer(socket_ptr& sp) :m_buf(512, 0), socket(sp) {
		lasttime = std::chrono::system_clock::now();
		peerName = socket->remote_endpoint().address().to_string() + ":" + std::to_string(socket->remote_endpoint().port());
		respondStr = string("欢迎 ") + peerName;
		socket->async_write_some(buffer(respondStr), boost::bind(&this_type::write_handler, this, boost::asio::placeholders::error));
	}
	void close() {
		std::cout << "关闭socket\n\n" << std::endl;
		state = "close";
		socket->shutdown(socket_type::shutdown_both);
		socket->close();
	}
	string getPeerName() {
		return peerName;
	}
	void write_handler(const system::error_code& ec) {
		if (isclose()) return;
		if (ec) {
			cout << "send fail" << ec.message() << " receiver:" << peerName << endl;
			cout << "close client:" << peerName << endl;
			state = "close";
			return;
		}
		cout << "response msg ok，receiver:" << peerName << endl;
		socket->async_read_some(buffer(m_buf), bind(&this_type::read_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
	}
	void read_handler(const system::error_code& ec, std::size_t bytes_transferred) {
		if (isclose()) return;
		if (ec) {
			cout << "close client:" << peerName << endl;
			state = "close";
			return;
		}
		lasttime = std::chrono::system_clock::now();

		recvData += string(&m_buf.front(), bytes_transferred);
		std::cout << "接受的原始数据:" << recvData << ",传送字节大小为:" << bytes_transferred << std::endl;
		std::string::size_type idx = recvData.find(suffix);
		if (idx != std::string::npos) {
			string package = recvData.substr(0, idx);
			recvData = recvData.substr(idx + suffix.size());//删除接收到的数据包部分
			boost::trim(package);
			respData = processPackage(std::move(package));
			socket->async_write_some(buffer(respData), boost::bind(&this_type::write_handler, this, boost::asio::placeholders::error));
		}
		else {//如果客户端不发送数据包结束后缀，会造成接收的数据包无限增大，加入数据自动废弃功能
			if (recvData.size() > 255) {
				recvData.clear();
				socket->async_write_some(buffer("data is too long,auto delete."), boost::bind(&this_type::write_handler, this, boost::asio::placeholders::error));
			}
			else {//为收到完整数据包，继续接收数据
				socket->async_read_some(buffer(m_buf), bind(&this_type::read_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
			}
		}
	}
	//处里客户端发来的请求，其中数值计算逻辑可执行程序比如EXEU 通过argv把客户数据传递过去，获得计算结果返回客户端
	string processPackage(const string package) {
		cout << "Recive data from:" << peerName << " data:" << package << endl;
		vector<string> vec;
		split(vec, package, is_any_of(delims), token_compress_on);
		string result;
		try {
			double sum = 0;
			for (size_t i = 0; i < vec.size(); i++) {
				//cout<<vec[i]<<":"<<std::stod(vec[i])<<endl;
				sum += std::stod(vec[i]);
			}
			result = boost::join(vec, "+") + "=" + boost::lexical_cast<string>(sum);
		}
		catch (std::exception e) {
			result = "发送的数据包内包含非法数字";
		}
		return std::move(result);
	}
	bool isclose() {
		return state == "close";
	}
	bool isexpire() {
		return (std::chrono::system_clock::now() - lasttime) > deadline;
	}
};
class Server {//网络服务业务逻辑全部在Server类中，客户端连接后自动创建一个peer
	typedef Server this_type;
private:
	io_service m_io;
	acceptor_type m_acceptor;
	boost::asio::steady_timer timer;
	std::map<string, std::shared_ptr<Peer>> m_peers;
	void startTimer() {
		timer.expires_from_now(std::chrono::seconds(1));
		timer.async_wait(boost::bind(&this_type::onTimer, this, boost::asio::placeholders::error));
	}
	void onTimer(const system::error_code&) {
		//先进行超时处理关闭socke，然后隔一段时间后移除socket
		for (auto iter = m_peers.begin(); iter != m_peers.end();) {
			if (iter->second->isclose()) {
				cout << iter->second->getPeerName() << " 移除socket:" << iter->second.use_count() << std::endl;
				m_peers.erase(iter++);
				cout << "移除socket成功!" << endl;
			}
			else if (iter->second->isexpire()) {
				cout << "超时处理,关闭socket。。。。" << std::endl;
				iter->second->close();
				iter++;
			}
			else {
				iter++;
			}
		}
		startTimer();
	}

public:
	Server() :timer(m_io), m_acceptor(m_io, endpoint_type(ip::address_v4::from_string(ipaddress), port))
	{
		startTimer();
		accept();
	}
	~Server() {
		m_acceptor.close();
	}

	void run() {
		m_io.run();
	}
	void accept() {
		socket_ptr socket(new socket_type(m_io), deleter<socket_type>);
		m_acceptor.async_accept(*socket, boost::bind(&this_type::accept_handler, this, boost::asio::placeholders::error, socket));
	}
	void accept_handler(const system::error_code& ec, socket_ptr socket) {
		if (ec) {
			return;
		}
		auto peer = make_shared<Peer>(socket);//客户端新连接，创建一个对等peer实例，提供服务
		cout << "new client:" << peer->getPeerName() << endl;
		m_peers.emplace(make_pair(peer->getPeerName(), peer));
		accept();//继续接收新的网络连接
	}
};


int main(int argc, char *argv[])
{
	try {
		cout << "Server start ." << endl;
		Server srv;
		srv.run();
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	cout << "Server end." << endl;
	return 0;
}
